package com.example.demo.runner;

import com.example.demo.entity.User;
import com.example.demo.game.GameMgrInterface;
import com.example.demo.runner.code.CodeRunnerInterface;
import com.example.demo.runner.code.RunnerFactory;
import com.example.demo.runner.code.RunnerMessage;

import java.io.IOException;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;


public class GameRunner {
    private final Map<User, CodeRunnerInterface> runners = new HashMap<>();
    private final Executor executor = Executors.newSingleThreadExecutor();
    private final GameMgrInterface mgr;

    public GameRunner(GameMgrInterface mgr) {
        this.mgr = mgr;
        List<User> users = mgr.getUsers();
        for (User user :
            users) {
            try {
                runners.put(user, RunnerFactory.create("C", user));
            } catch (Exception e) {
                // TODO
                return;
            }
        }
    }

    public List run() throws InterruptedException {
        runners.forEach((user, v) -> {
            try {
                v.compile();
                executor.execute(v);
            } catch (IOException e) {
                // TODO
                System.out.println("err: user = " + user);
                e.printStackTrace();
            }
        });

        List<Map> frames = new LinkedList<>();

        String stateRawStr = mgr.getState();
        frames.add(mgr.getCanvas());
        RunnerMessage state = new RunnerMessage(stateRawStr);
        while (!mgr.isDone()) {
            // get new states
            for (CodeRunnerInterface v : runners.values()) {
                v.getInputQueue().put(state);
            }

            Map<User, String> outputs = new HashMap<>(runners.size());

            for (Map.Entry<User, CodeRunnerInterface> entry : runners.entrySet()) {
                User user = entry.getKey();
                CodeRunnerInterface runner = entry.getValue();
                String action = mgr.parseAction(user, runner.getOutputQueue());
                System.out.println("action = " + action);
                // TODO debug info from error stream
                outputs.put(user, action);
            }

            // update game manager
            try {
                stateRawStr = mgr.step(outputs);
            } catch (Exception e) {
                // TODO
                e.printStackTrace();
                break;
            }
            state = new RunnerMessage(stateRawStr);
            frames.add(mgr.getCanvas());
        }
        for (CodeRunnerInterface runner : runners.values()) {
            runner.stop();
        }
        System.out.println("game runner stop");
        return frames;
    }
}
